package com.nolon.flange.repository

import android.app.Application
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import com.nolon.flange.AppDatabase
import com.nolon.flange.dao.*
import com.nolon.flange.model.*
import java.util.*

class FlangeRepository(application: Application) {
    private val database: AppDatabase by lazy { AppDatabase.getInstance(application) }

    private val projectDao: ProjectDao by lazy { database.projectDao }

    private val orderDao: OrderDao by lazy { database.orderDao }

    private val rawInfoDao: RawInfoDao by lazy { database.rawInfoDao }

    private val rawSolutionDao: RawSolutionDao by lazy { database.rawSolutionDao }

    private val partDao: PartDao by lazy { database.partDao }

    private var solutions: List<RawSolution>? = null

    fun getRawSolutionsByProjectId(projectId: Int): LiveData<List<RawSolution>> {
        return rawSolutionDao.getAllByProjectIdLive(projectId)
    }

    val allProjects: LiveData<List<Project>> by lazy { projectDao.getAll() }

    suspend fun insertProject(project: Project): Long = projectDao.insert(project)

    suspend fun updateProject(project: Project) = projectDao.update(project)

    suspend fun deleteProject(project: Project) = projectDao.delete(project)

    suspend fun getProject(projectId: Int) = projectDao.getProject(projectId)

    suspend fun insertOrderItem(order: Order) = orderDao.insert(order)

    suspend fun insertOrderItems(orders: List<Order>) = orderDao.insertAll(orders)


    suspend fun updateOrder(order: Order) = orderDao.update(order)

    suspend fun deleteOrder(order: Order) = orderDao.delete(order)

    fun getOrdersByProjectId(projectId: Int): LiveData<List<Order>> =
        orderDao.getOrdersByProjectId(projectId)

    fun getRawInfosByProjectId(projectId: Int): LiveData<List<RawInfo>> =
        rawInfoDao.getRawsByProjectId(projectId)

    suspend fun deleteProjectById(id: Int) {
        projectDao.deleteById(id)
        orderDao.deleteOrdersByProjectId(id)
        rawInfoDao.deleteAllByProjectId(id)
        rawSolutionDao.deleteAllByProjectId(id)
        partDao.deleteAllByProjectId(id)
    }

    suspend fun deleteAllRawsByProjectId(projectId: Int) =
        rawInfoDao.deleteAllByProjectId(projectId)

    suspend fun insertRaw(rawInfo: RawInfo) = rawInfoDao.insert(rawInfo)

    suspend fun insertAllRaws(rawInfos: List<RawInfo>) = rawInfoDao.insertAll(rawInfos)

    suspend fun insertRawSolution(rawSolution: RawSolution) = rawSolutionDao.insert(rawSolution)

    suspend fun insertParts(parts: List<Part>) = partDao.insertAll(parts)

    suspend fun deleteRawById(id: Int) = rawInfoDao.deleteById(id)

    suspend fun updateRaws(rawInfoList: List<RawInfo>) =
        rawInfoList.forEach { rawInfoDao.update(it) }

    suspend fun saveSolutions(solutions: ArrayList<RawSolution>) {
        solutions.forEach {
            val solutionId = insertRawSolution(it)
            it.parts.forEach { p ->
                partDao.insert(p.copy(projectId = it.projectId, solutionId = solutionId.toInt()))
            }
        }
        val raws = solutions.map { it.count }.reduce { acc, i -> acc + i }
        val rawsLen = solutions.map { it.count * it.rawInfo.length }.reduce { acc, fl -> acc + fl }
        val cost = solutions.map { it.count * it.rawInfo.unitPrice }.reduce { acc, fl -> acc + fl }
        val waste = solutions.map { it.tailLength * it.count }.reduce { acc, fl -> acc + fl }
        val p = getProject(solutions[0].projectId).copy(
            solutionCount = solutions.size,
            hasSolution = true,
            rawCount = solutions.map { it.count }.reduce { acc, i -> acc + i },
            usageRate = 1f - waste.toFloat() / rawsLen,
            totalCost = cost,
            modified = System.currentTimeMillis()
        )
        updateProject(p)
    }

    fun getPartsByProjectIdGroupedBySolutionId(projectId: Int): LiveData<Map<Int, List<Part>>> {
        return Transformations.map(partDao.getAllByProjectId(projectId)) { list ->
            list.groupBy { it.solutionId }
        }
    }

    fun getPartsByProjectId(projectId: Int): LiveData<List<Part>> {
        return partDao.getAllByProjectId(projectId)
    }

    suspend fun deleteSolutionsByProjectId(projectId: Int) {
        rawSolutionDao.deleteAllByProjectId(projectId)
        partDao.deleteAllByProjectId(projectId)
    }

    fun getProjectLive(projectId: Int): LiveData<Project> {
        return projectDao.getProjectLive(projectId)
    }

//    suspend fun deleteRawByDetails(projectId: Int, materialSpec: String, actualSpec: Float) = rawDao.deleteRawByDetails(projectId,materialSpec,actualSpec)

}

